#include "Driver.h"
#include <stdarg.h>
using namespace std;
using namespace aed2;

// auxiliares para tests
#include <iostream>
#include <sstream>
void mt_assert(bool expr, string lhs, string rhs, int line) { if (!expr) {ostringstream os;os << "error en linea " << line << endl;os << "   se esperaba: " << rhs << endl;os << "     se obtuvo: " << lhs;throw os.str();} }
void mt_str_assert(string lhs, string rhs, int line) { if (lhs.compare(rhs) != 0) { ostringstream os;os << "error en linea " << line << endl;os << "   se esperaba: " << rhs << endl;os << "     se obtuvo: " << lhs;throw os.str();} }
#define RUN_TEST(test) {\
	{bool ok = true;\
	cout << #test << "..." << flush;\
	try { test(); }\
	catch (string& msg) { ok = false; cout << msg; } \
	catch (...) { ok = false; cout << "error"; }\
	if (ok) { cout << "ok"; }\
	cout << endl << flush;\
	}\
}
#define ASSERT_EQ(lhs, rhs) {{ostringstream lhss, rhss;lhss << (lhs);rhss << (rhs);mt_assert((lhs) == (rhs), lhss.str(), rhss.str(), __LINE__);}}
#define ASSERT(expr) { mt_assert((expr), "false", "true", __LINE__); }
#define ASSERT_STR_EQ(lhs, rhs) { mt_str_assert(string((lhs)), string((rhs)), __LINE__); }
// fin auxiliares para tests

/**
 * Permite construir una lista de strings de forma comoda.
 * l("lorem", "ipsum", NULL) equivale a la lista ["lorem", "ipsum"]
 * l(NULL) equivale a la lista vacia.
 *
 * siembre es necesario poner un NULL para indicar el final de la lista
 */
Lista<string> l(const char* t1, ...) {
	Lista<string> res;
	va_list ap;
	const char* t = t1;
	va_start(ap,t1);
	while (t != NULL) {
		res.AgregarAtras(t);
		t = va_arg(ap,const char *);
	}
	va_end(ap);
	return res;
}

void construccionListas() {
	Lista<string> l1 = l("tag1", "tag2", NULL);
	ASSERT_EQ(l1.Longitud(), 2);
	ASSERT_STR_EQ(l1.Primero(), "tag1");
	ASSERT_STR_EQ(l1.Ultimo(), "tag2");

	Lista<string> l2 = l(NULL);
	ASSERT_EQ(l2.Longitud(), 0);
}

void driverCtor() {
	Driver d;
}

void driverPuedeAgregarPaises() {
	Driver d;
	d.agregarPaises(5);
	d.agregarPaises(3);
}

void armarReglas1(Driver& d) {
	// <{1,2,3}, Id U {1->2, 2->3}>
	d.agregarPaises(3);
	d.restringirVer(1, 3);
	d.restringirVer(2, 1);
	d.restringirVer(3, 1);
	d.restringirVer(3, 2);
}

void armarReglas2(Driver& d) {
	// <{1,2}, Id U {1->2}>
	d.agregarPaises(2);
	d.restringirVer(2, 1);
}

void driverRegistraUsuariosYPublica() {
	Driver d;
	armarReglas1(d);
	d.registrar("fulano", 1);

	//std::cout << &d.redSocial->usuarios << " " << &d.redSocial->tags.dicc << std::endl;

	d.publicar("fulano", "lorem ipsum dolor", l("tag1", "tag2", NULL));
}

void SiyNoQuiereSerAmigo() {
	Driver d;
	armarReglas1(d);
	d.registrar("fulano", 1);
	d.registrar("sultano", 1);

	ASSERT_EQ(d.quiereSerAmigo("fulano", "sultano"), false);


	ASSERT_EQ(d.quiereSerAmigo("fulano", "sultano"), false);

	d.solicitaAmistad("fulano", "sultano");

	ASSERT_EQ(d.quiereSerAmigo("fulano", "sultano"), true);
	ASSERT_EQ(d.quiereSerAmigo("sultano", "fulano"), false);

}

void solicitudesSimetricasHacenAmigosNoTienenSolicitudPendiente() {
	Driver d;
	armarReglas1(d);
	d.registrar("fulano", 1);
	d.registrar("sultano", 1);

	d.solicitaAmistad("fulano", "sultano");
	d.solicitaAmistad("sultano", "fulano");

	ASSERT_EQ(d.quiereSerAmigo("fulano", "sultano"), false);
	ASSERT_EQ(d.quiereSerAmigo("sultano", "fulano"), false);

	ASSERT_EQ(d.sonAmigos("fulano", "sultano"), true);
	ASSERT_EQ(d.sonAmigos("sultano", "fulano"), true);
}

void hacerAmigos(Driver &d, string u1, string u2) {
	d.solicitaAmistad(u1, u2);
	d.solicitaAmistad(u2, u1);
}

void soloAmigoVeMensaje() {
	Driver d;
	armarReglas1(d);
	d.registrar("fulano", 1);
	d.registrar("mengano", 1);
	hacerAmigos(d, "fulano", "mengano");

	ASSERT_EQ(d.cantidadMensajesVisibles("mengano"), 0);
	ASSERT_EQ(d.cantidadMensajesVisibles("fulano"), 0);

	d.publicar("fulano", "viejo!", l(NULL));
	d.publicar("fulano", "hola!", l("tag1", "tag2", NULL));

	ASSERT_EQ(d.cantidadMensajesVisibles("mengano"), 2);
	ASSERT_EQ(d.cantidadMensajesVisibles("fulano"), 0);

	ASSERT_STR_EQ(d.mensajeVisible("mengano", 0), "fulano> hola! #tag1 #tag2");
	ASSERT_STR_EQ(d.mensajeVisible("mengano", 1), "fulano> viejo!");
}


// TODO: quitar estos tests

void noAmigosOPendientesEnAlgunSentidoNoVenMensajes() {
	Driver d;
	armarReglas1(d);
	d.registrar("fulano", 1);
	d.registrar("noamigo", 1);
	d.registrar("quiere", 1);
	d.registrar("loquiere", 1);

	d.quiereSerAmigo("quiere", "fulano");
	d.quiereSerAmigo("fulano", "loquiere");

	d.publicar("fulano", "hola!", l(NULL));

	ASSERT_EQ(d.cantidadMensajesVisibles("noamigo"), 0);
	ASSERT_EQ(d.cantidadMensajesVisibles("quiere"), 0);
	ASSERT_EQ(d.cantidadMensajesVisibles("loquiere"), 0);
}

void amigosVenMensajesPreviosALaAmistad() {
	Driver d;
	armarReglas1(d);
	d.registrar("fulano", 1);
	d.publicar("fulano", "lorem ipsum", l("dolor", "sit", "amet", NULL));
	ASSERT_EQ(d.cantidadMensajesPublicados("fulano"), 1);
	d.registrar("mengano", 1);
	ASSERT_EQ(d.cantidadMensajesVisibles("mengano"), 0);
	hacerAmigos(d, "mengano", "fulano");
	ASSERT_EQ(d.cantidadMensajesVisibles("mengano"), 1);
	ASSERT_STR_EQ(d.mensajeVisible("mengano", 0), "fulano> lorem ipsum #dolor #sit #amet");
}

void lasRestriccionesOcultanMensajes() {
	Driver d;
	armarReglas2(d);
	d.registrar("a", 1);
	d.registrar("b", 2);
	hacerAmigos(d, "a", "b");

	d.publicar("a", "b no me ve", l(NULL));
	d.publicar("b", "a si me ve", l(NULL));

	ASSERT_EQ(d.cantidadMensajesVisibles("a"), 1);
	ASSERT_STR_EQ(d.mensajeVisible("a", 0), "b> a si me ve");
	ASSERT_EQ(d.cantidadMensajesVisibles("b"), 0);
}


void lasRestriccionesOcultanMensajesViejosAlHacerAmigo() {
	Driver d;
	armarReglas2(d);
	d.registrar("a", 1);
	d.registrar("b", 2);

	d.publicar("a", "b no me ve", l(NULL));
	d.publicar("b", "a si me ve", l(NULL));

	ASSERT_EQ(d.cantidadMensajesVisibles("a"), 0);
	ASSERT_EQ(d.cantidadMensajesVisibles("b"), 0);

	hacerAmigos(d, "a", "b");

	ASSERT_EQ(d.cantidadMensajesVisibles("a"), 1);
	ASSERT_STR_EQ(d.mensajeVisible("a", 0), "b> a si me ve");
	ASSERT_EQ(d.cantidadMensajesVisibles("b"), 0);
}

void unMensajeRecienteSeVe() {
	Driver d;
	armarReglas1(d);
	d.registrar("fulano", 1);
	d.registrar("mengano", 1);
	hacerAmigos(d, "fulano", "mengano");

	d.publicar("fulano", "otro mensaje!", l("soy-reciente", NULL));

	ASSERT_EQ(d.cantidadMensajesVisibles("mengano"), 1);
	ASSERT_EQ(d.cantidadMensajesRecientes("mengano"), 1);

	ASSERT_STR_EQ(d.mensajeVisible("mengano", 0), "fulano> otro mensaje! #soy-reciente");
	ASSERT_STR_EQ(d.mensajeReciente("mengano", 0), "fulano> otro mensaje! #soy-reciente");
}

void unMensajeViejoNoEsReciente() {
	Driver d;
	armarReglas1(d);
	d.registrar("fulano", 1);
	d.registrar("mengano", 1);
	hacerAmigos(d, "fulano", "mengano");

	ASSERT_EQ(d.cantidadMensajesVisibles("mengano"), 0);
	ASSERT_EQ(d.cantidadMensajesRecientes("mengano"), 0);
	ASSERT_EQ(d.cantidadMensajesVisibles("fulano"), 0);
	ASSERT_EQ(d.cantidadMensajesRecientes("fulano"), 0);

	d.publicar("fulano", "dime", l("no-sere-reciente", NULL));

	ASSERT_EQ(d.cantidadMensajesVisibles("mengano"), 1);
	ASSERT_EQ(d.cantidadMensajesRecientes("mengano"), 1);
	ASSERT_EQ(d.cantidadMensajesVisibles("fulano"), 0);
	ASSERT_EQ(d.cantidadMensajesRecientes("fulano"), 0);

	d.avanzarDia(7);
	d.publicar("mengano", "me :-P", l(NULL));

	ASSERT_EQ(d.cantidadMensajesVisibles("mengano"), 1);
	ASSERT_EQ(d.cantidadMensajesRecientes("mengano"), 1);
	ASSERT_EQ(d.cantidadMensajesVisibles("fulano"), 1);
	ASSERT_EQ(d.cantidadMensajesRecientes("fulano"), 1);

	d.publicar("fulano", "gracioso", l(NULL));

	ASSERT_EQ(d.cantidadMensajesVisibles("mengano"), 2);
	ASSERT_EQ(d.cantidadMensajesRecientes("mengano"), 1);
	ASSERT_EQ(d.cantidadMensajesVisibles("fulano"), 1);
	ASSERT_EQ(d.cantidadMensajesRecientes("fulano"), 1);
}

void ceroPaisesNoTienenHueco() {
	Driver d;
	ASSERT_EQ(d.tieneAlgunHueco(), false);
}

void unSoloPaisNoTieneHueco() {
	Driver d;
	d.agregarPaises(1);
	ASSERT_EQ(d.tieneAlgunHueco(), false);
}

void reglasCompletasNoTienenHueco() {
	Driver d;
	d.agregarPaises(10);
	ASSERT_EQ(d.tieneAlgunHueco(), false);
}

void tieneAlgunHueco1() {
	Driver d;
	armarReglas1(d);
	ASSERT_EQ(d.tieneAlgunHueco(), true);
}

void noPierdeMemoriaAlPublicar() {
	Driver d;
	armarReglas1(d);
	d.registrar("jdoe", 1);
	d.publicar("jdoe", "lorem ipsum", l("t1", "t2", NULL));
}

void tagsDejanDeSerRecientes() {
	Driver d;
	armarReglas1(d);
	d.registrar("jdoe", 1);
	d.registrar("sandy", 1);
	hacerAmigos(d, "jdoe", "sandy");
	d.publicar("jdoe", "foo", l("t1", NULL));
	d.publicar("jdoe", "bar", l("t1", "t2", NULL));

	ASSERT_EQ(d.cantidadTagsRecientes(), 2);
	ASSERT_EQ(d.esTagReciente("t1"), true);
	ASSERT_EQ(d.esTagReciente("t2"), true);

	ASSERT_EQ(d.repeticionesRecientesTag("t1"), 2);
	ASSERT_EQ(d.repeticionesRecientesTag("t2"), 1);

	d.avanzarDia(7);
	d.publicar("jdoe", "baz", l("t2", NULL));

	ASSERT_EQ(d.cantidadTagsRecientes(), 1);
	ASSERT_EQ(d.esTagReciente("t1"), false);
	ASSERT_EQ(d.esTagReciente("t2"), true);
	ASSERT_EQ(d.repeticionesRecientesTag("t2"), 1);
}

void casoBorde_SoportaStringsVaciosComoUsuarios() {
	Driver d;
	armarReglas1(d);
	d.registrar("", 1);
	d.registrar("a", 1);

	d.solicitaAmistad("", "a");
	d.solicitaAmistad("a", "");

	ASSERT(d.sonAmigos("", "a"));
}

void frecuenciaConstanteDeTagsSeMantieneBien() {
	Driver d;
	armarReglas1(d);
	d.registrar("a", 1);

	for(int i = 1; i <= 7; i++) {
		d.avanzarDia(1);
		d.publicar("a", "foo", l("t1", NULL));
		ASSERT_EQ(d.cantidadTagsRecientes(), 1);
		ASSERT(d.esTagReciente("t1"));
		ASSERT_EQ(d.repeticionesRecientesTag("t1"), i);
	}

	for(int i = 1; i <= 30; i++) {
		d.avanzarDia(1);
		d.publicar("a", "foo", l("t1", NULL));
		ASSERT_EQ(d.cantidadTagsRecientes(), 1);
		ASSERT(d.esTagReciente("t1"));
		ASSERT_EQ(d.repeticionesRecientesTag("t1"), 7)
	}
}

int main(void) {
	RUN_TEST(construccionListas);
	RUN_TEST(driverCtor);
	RUN_TEST(driverPuedeAgregarPaises);
	RUN_TEST(driverRegistraUsuariosYPublica);
    RUN_TEST(SiyNoQuiereSerAmigo);
	RUN_TEST(solicitudesSimetricasHacenAmigosNoTienenSolicitudPendiente);
	RUN_TEST(soloAmigoVeMensaje);
	RUN_TEST(ceroPaisesNoTienenHueco);
	RUN_TEST(tieneAlgunHueco1);
    RUN_TEST(noPierdeMemoriaAlPublicar);
	RUN_TEST(unMensajeRecienteSeVe);
	RUN_TEST(unMensajeViejoNoEsReciente);
	RUN_TEST(tagsDejanDeSerRecientes);
	RUN_TEST(casoBorde_SoportaStringsVaciosComoUsuarios);
	RUN_TEST(noAmigosOPendientesEnAlgunSentidoNoVenMensajes);
	RUN_TEST(amigosVenMensajesPreviosALaAmistad);
	RUN_TEST(lasRestriccionesOcultanMensajes);
	RUN_TEST(lasRestriccionesOcultanMensajesViejosAlHacerAmigo);
	RUN_TEST(frecuenciaConstanteDeTagsSeMantieneBien);
	return 0;
}
